﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace RWLibrary.Parser
{
    class QueryScanner
    {

        private string str; // cały program źródłowy
        private int pos; // pozycja analizowanego znaku w programie źródłowym
        public int Pos
        {
            get { return pos; }
        }

        public QueryScanner(Stream s)
        {
            var sr = new StreamReader(s);
            str = sr.ReadToEnd();
            pos = 0;
        }

        public QueryScanner(String s)
        {
            str = s;
            pos = 0;
        }


        public QueryToken Scan()
        {
            QueryToken tt = null;
            while (pos < str.Length && (str[pos] == ' ' || str[pos] == '\r')) ++pos;
            if (pos == str.Length)
                return new QueryToken(QueryToken.Eof, "Eof");
            if( (str[pos] >= 'a' && str[pos] <= 'z') || (str[pos] >= 'A' && str[pos] <= 'Z')) // rozpoznawanie identyfikatorów i słów kluczowych
            {
                tt = new QueryToken(QueryToken.Ident, str[pos].ToString());
                    // początkowo zakładamy, że jest to identyfikator
                while (++pos < str.Length &&
                       ((str[pos] >= 'a' && str[pos] <= 'z') ||(str[pos] >= 'A' && str[pos] <= 'Z') || (str[pos] >= '0' && str[pos] <= '9')))
                    tt.val += str[pos].ToString();
                switch (tt.val) // rozpoznawanie słów kluczowych
                {
                    case "after":
                        tt.kind = QueryToken.After;
                        break;
                    case "accessible":
                        tt.kind = QueryToken.Accessible;
                        break;
                    case "goal":
                        tt.kind = QueryToken.Goal;
                        break;
                    case "cost":
                        tt.kind = QueryToken.Cost;
                        break;
                    case "if":
                        tt.kind = QueryToken.If;
                        break;
                    case "from":
                        tt.kind = QueryToken.From;
                        break;
                    case "necessary":
                        tt.kind = QueryToken.Necessary;
                        break;
                    case "possibly":
                        tt.kind = QueryToken.Possibly;
                        break;
                    case "with":
                        tt.kind = QueryToken.With;
                        break;
                }
                return tt;
            }
            if (str[pos] >= '0' && str[pos] <= '9') // rozpoznawanie liczb
            {
                tt = new QueryToken(QueryToken.Number, str[pos].ToString());
                    // początkowo zakładamy, że jest to liczba całkowita
                while (++pos < str.Length && str[pos] >= '0' && str[pos] <= '9')
                    tt.val += str[pos].ToString();
                //if (pos < str.Length && str[pos] == '.') // jednak liczba zmiennopozycyjna
                //{
                //    tt.kind = QueryToken.Number_d;
                //    tt.val += ".";
                //    if (!(++pos < str.Length && str[pos] >= '0' && str[pos] <= '9'))
                //        tt.kind = QueryToken.Error;
                //    else
                //        do
                //            tt.val += str[pos].ToString(); while (++pos < str.Length && str[pos] >= '0' &&
                //                                                  str[pos] <= '9');
                //}
                return tt;
            }
            switch (str[pos]) // rozpoznawanie operatorów i innych symboli
            {
                case '!':
                    tt = new QueryToken(QueryToken.Negation, "!");
                    break;
                case '&':
                    if (str[pos + 1] != str[pos])
                        tt = new QueryToken(QueryToken.Error, string.Empty);
                    else
                    {
                        tt = new QueryToken(QueryToken.And, "&&");
                        pos++;
                    }
                    break;
                case '|':
                    if (str[pos + 1] != str[pos])
                        tt = new QueryToken(QueryToken.Error, string.Empty);
                    else
                    {
                        tt = new QueryToken(QueryToken.Or, "|");
                        pos++;
                    }
                    break;
                case '=':
                    if (str[pos + 1] != '>')
                        tt = new QueryToken(QueryToken.Error, string.Empty);
                    else
                    {
                        tt = new QueryToken(QueryToken.Implication, "=>");
                        pos++;
                    }
                    break;
                case '<':
                    if (str[pos + 1] != '=' && str[pos + 2] != '>')
                        tt = new QueryToken(QueryToken.Error, string.Empty);
                    else
                    {
                        tt = new QueryToken(QueryToken.Iif, "<=>");
                        pos += 2;
                    }
                    break;
                case '(':
                    tt = new QueryToken(QueryToken.Open_par, "(");
                    break;
                case ')':
                    tt = new QueryToken(QueryToken.Close_par, ")");
                    break;
                case '\n':
                    tt = new QueryToken(QueryToken.Endl, "Endl");
                    break;
                default:
                    tt = new QueryToken(QueryToken.Error, str[pos].ToString());
                    break;
            }
            ++pos;
            return tt;
        }

    }
}
